import { DataSource } from '@angular/cdk/table';
import { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs';
import { map, debounceTime, tap } from 'rxjs/operators';
import { filter as _filter, map as _map } from 'lodash';
import { FilterUtils, TreeFilterUtils } from '../core/api';
import { SelectFilterConfig, SelectFilterConfig as FilterConfig } from './interface';

export class SelectDataSource<T> extends DataSource<T> {
  // 是否为异步，异步时认为data永远是经过条件处理的
  async: boolean;

  // 源数据
  private readonly _data$: BehaviorSubject<T[]>;

  // renderdata流，依赖data变更
  private readonly _renderData$ = new Subject<T[]>();

  // 转换后的数据
  private readonly _selectData$: BehaviorSubject<any[]> = new BehaviorSubject(null);

  // 过滤条件
  private readonly _filter$: BehaviorSubject<FilterConfig> = new BehaviorSubject(null);

  constructor(initialData: T[] = [], async: boolean = false) {
    super();
    this.async = async;
    this._data$ = new BehaviorSubject<T[]>(initialData);
    this._updateChangeSubscription();
  }

  // 源数据
  get data(): T[] {
    return this._data$.value;
  }

  // 转换后的数据
  get selectData(): any[] {
    return this._selectData$.value;
  }

  get filter(): SelectFilterConfig {
    return this._filter$.value;
  }

  set data(data: T[]) {
    this._data$.next(data);
  }

  // 过滤
  set filter(data: FilterConfig) {
    this._filter$.next(data);
  }

  disconnect(): void {}

  connect(): Subject<any> {
    return this._renderData$;
  }

  _filterTool(data, filter): any {
    return FilterUtils[filter.filterMode](data, filter);
  }

  // NEED_DO: 优化 data传给用户的是OptionItem类型
  _getDataStream(): Observable<any> {
    return this._data$.pipe(
      map((res): any => this._converData(res)),
      tap((res): void => this._selectData$.next(res)),
    );
  }

  // NEED_DO: 过滤了子级 在重新搜索就有问题了，只能支持一级搜索
  _loopFilter(data, filter): any {
    return _filter(data, (item): any => {
      const { length } = this._filterTool([item], filter);
      if (length) {
        return true;
      }
      if (!item.children || !item.children.length) {
        return false;
      }
      item.children = this._loopFilter(item.children, filter);
      return item.children.length;
    });
  }

  _converData(data: T[], parent?): any {
    return _map(data, (item): any => {
      item.children = item.children ? this._converData(item.children, item) : item.children;
      return {
        ...item,
        ...{
          originData: item,
          hiddenMatch: false,
          parent,
          active: false,
          selected: false,
        },
      };
    });
  }

  _updateChangeSubscription(): void {
    const dataStream = this._getDataStream();
    const filterStream = this._filter$;

    // tslint:disable-next-line:deprecation
    combineLatest(dataStream, filterStream)
      .pipe(
        debounceTime(1),
        map(([data, filter]): any => {
          if (this.async) {
            return data;
          }
          return this._filterData(data, filter);
        }),
      )
      .subscribe((res): void => {
        this._renderData$.next(res);
      });
  }

  // tslint:disable-next-line
  _filterData<U>(data: U[], filter: FilterConfig | null): U[] {
    const traversalFilter = (): U[] => TreeFilterUtils.filter(data, filter, filter.customItemFilter, 'children');

    if (!filter || !Object.keys(filter).length) {
      return data;
    } else if (filter.customItemFilter) {
      return traversalFilter();
    } else if (filter.customFilter) {
      return filter.customFilter(data, filter);
    } else if (!FilterUtils[filter.filterMode]) {
      return data;
    } else {
      return traversalFilter();
    }
  }
}
